package com.lenovo.redission.redission.config.lock;

import com.lenovo.redission.redission.lock.IDistributedLock;
import lombok.extern.log4j.Log4j2;

import java.util.Arrays;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;

/**
 * <p>
 * Description:锁
 * <p>
 * Copyright: 天津联想协同科技有限公司
 * <p>
 *
 * @author 赵希坤 zhaoxikun
 * @date 2021/5/31 4:40 下午
 */
@Log4j2
public class LockUtil {

    private IDistributedLock redisDistributedLock;

    protected LockUtil(IDistributedLock redisDistributedLock) {
        this.redisDistributedLock = redisDistributedLock;
    }

    private String errorParams(String function, Object... params) {
        StringBuilder sb = new StringBuilder();
        sb.append(function).append(" ");
        if (null != params) {
            for (Object param : params) {
                String paramsString = null;
                if (null != param) {
                    paramsString = param.getClass().isArray() ? Arrays.toString((Object[]) param) : param.toString();
                }
                sb.append(paramsString).append(" ");
            }
        }
        return sb.toString();
    }

    /**
     * 以阻塞方式的获取锁
     *
     * @param lockKey 锁名称
     * @return bizModule 结果Optional  null=没获取到锁
     */
    public <T, R> Optional<R> lock(String lockKey, T t, Function<T, R> bizModule) {
        try {
            redisDistributedLock.lock(lockKey);
            return Optional.ofNullable(bizModule.apply(t));
        } finally {
            redisDistributedLock.unlock(lockKey);
        }
    }

    /**
     * 获取锁 立即返回
     * 有看门狗，锁续命
     *
     * @param lockKey 锁名称
     * @return bizModule 结果Optional  null=没获取到锁
     */
    public <T, R> Optional<R> tryLock(String lockKey, T t, Function<T, R> bizModule) {
        return tryLock(lockKey, -1L, t, bizModule);
    }

    /**
     * 获取锁 立即返回
     *
     * @param lockKey    锁名称
     * @param expireTime 过期时间 ms 若 != -1 就没有看门狗，会出现代码没有执行完，锁结束了
     * @return bizModule 结果Optional  null=没获取到锁
     */
    public <T, R> Optional<R> tryLock(String lockKey, long expireTime, T t, Function<T, R> bizModule) {
        boolean lock = false;
        try {
            lock = redisDistributedLock.tryLock(lockKey, expireTime, TimeUnit.MILLISECONDS);
            if (lock) {
                return Optional.ofNullable(bizModule.apply(t));
            }
        } catch (InterruptedException e) {
            log.error(errorParams("tryLock", lockKey, expireTime, t), e);
        } finally {
            if (lock) {
                redisDistributedLock.unlock(lockKey);
            }
        }
        return null;
    }

    /**
     * 尝试获取锁 超时返回
     * 有看门狗，锁续命
     *
     * @param lockKey  锁名称
     * @param waitTime 超时时间 ms
     * @return bizModule 结果Optional  null=没获取到锁
     * @throws InterruptedException
     */
    public <T, R> Optional<R> tryLockWait(String lockKey, long waitTime, T t, Function<T, R> bizModule) throws InterruptedException {
        return tryLockWait(lockKey, -1L, waitTime, t, bizModule);
    }

    /**
     * 尝试获取锁 超时返回
     *
     * @param lockKey    锁名称
     * @param expireTime 过期时间 ms 若 != -1 就没有看门狗，会出现代码没有执行完，锁结束了
     * @param waitTime   超时时间 ms
     * @return bizModule 结果Optional  null=没获取到锁
     * @throws InterruptedException
     */
    public <T, R> Optional<R> tryLockWait(String lockKey, long expireTime, long waitTime, T t, Function<T, R> bizModule) throws InterruptedException {
        boolean lock = false;
        try {
            lock = redisDistributedLock.tryLockWait(lockKey, expireTime, waitTime, TimeUnit.MILLISECONDS);
            if (lock) {
                return Optional.ofNullable(bizModule.apply(t));
            }
        } catch (InterruptedException e) {
            log.error(errorParams("tryLock wait", lockKey, expireTime, waitTime, t), e);
        } finally {
            if (lock) {
                redisDistributedLock.unlock(lockKey);
            }
        }
        return null;
    }


}
